using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class Effect : IReusableClass
    {
        private static uint idGen = 0;

        public uint GUID;
        private BattleSceneEntityView entity;
        private bool isLoop; // 控制器表示是否重复播放
        private Action<Effect> onDestroy;
        private float duration;
        private bool playByManager;

        private float _curPlayTime;

        private string _effectPath;

        public Vector3 targetPos
        {
            set
            {
                if (this._controller)
                {
                    this._controller.targetPos = value;
                }
            }
        }

        public Vector3 targetScale
        {
            set
            {
                if (this._controller)
                {
                    this._controller.targetScale = value;
                }
            }
        }

        private EffectControlBehaviour _controller;

        private EffectInitParam _curPlayParam;
        private EffectControlBehaviour.EEffectStageType _curPlayStage = EffectControlBehaviour.EEffectStageType.All;

        public EffectControlBehaviour.EEffectStageType curPlayStage
        {
            get => this._curPlayStage;
            set
            {
                if (this._curPlayStage == EffectControlBehaviour.EEffectStageType.End &&
                    value != this._curPlayStage)
                {
                    return; // 若已经进入结束状态则不允许返回
                }
                this._curPlayStage = value;
                if (!this._controller)
                {
                    return;
                }
                this._controller.curPlayStage = this._curPlayStage;
            }
        }

        public void OnUpdate(float deltaTime)
        {
            if (!this._controller) // 用transform来判断是否正在播放
            {
                return;
            }
            this._curPlayTime += deltaTime;
            // 自身控制播放时的逐帧播放
            if (this.playByManager)
            {
                if (!this.isLoop)
                {
                    if (this._curPlayTime >= this.duration)
                    {
                        BattleEffectManager.Instance.Stop(this);
                    }
                }
                else if (this._controller && !this._controller.loop) // 控制器要求重复，但是资源不重复，自动重播
                {
                    if (this._curPlayTime >= this._controller.duration)
                    {
                        this._controller.RestartLoop();
                    }
                }

                this._controller.OnUpdate(deltaTime);

#if UNITY_EDITOR
                // 编辑器下模拟驱动
                if (!Application.isPlaying)
                {
                    this.Simulate(this._curPlayTime);
                }
#endif
            }
        }

        public bool OnEndUpdate(float deltaTime)
        {
            if (!this._controller)
            {
                return false;
            }
            this._controller.OnUpdate(deltaTime);
            return this._controller.isEnd;
        }

        public void Simulate(double curTime)
        {
            this._curPlayTime = (float)curTime;
            if (!this._controller)
            {
                return;
            }
            this._controller.ParticleSimulate(this._curPlayTime);
        }

        public void Initialize(uint nGUID = 0)
        {
            if (nGUID <= 0)
            {
                nGUID = GetInstId();
            }
            this.GUID = nGUID;
        }

        public void Play(EffectInitParam initParam)
        {
            this._curPlayParam = initParam;
            // this.targetPos = initParam.targetPos;
            this.isLoop = initParam.isLoop;
            this.onDestroy = initParam.onDestroy;
            this.playByManager = initParam.playByManager;

            this._curPlayTime = 0;
            if (!this._controller)
            {
                return;
            }

            if (!this.isLoop)
            {
                var fDuration = 3f;
                if (this._controller != null && this._controller.duration > 0)
                {
                    fDuration = this._controller.duration + 0.1f;
                }
                this.duration = fDuration;
            }

            this._controller.Play(initParam.targetPos, this.GetBindPoint, this.curPlayStage);
        }

        private void Clear()
        {
            this.onDestroy?.Invoke(this);

            if (this.entity != null)
            {
                if (this.entity.isLoaded)
                {
                    this.entity.Put();
                }
                this.entity = null;
            }
#if UNITY_EDITOR
            else if (!Application.isPlaying && this._controller)
            {
                BattleClientUtil.SafeDestroy(this._controller.gameObject);
            }
#endif
            this.GUID = 0;
            this.isLoop = false;
            this.onDestroy = null;
            this._controller = null;
            this._effectPath = default;
            // Bullet
            this.duration = default;

            this._curPlayTime = 0;
            this._curPlayStage = EffectControlBehaviour.EEffectStageType.All;
        }

        public uint MaxStore => 10;

        public void OnReset()
        {
            Clear();
            this._curPlayParam = default;
            this.playByManager = default; 
        }

        public async UniTaskVoid LoadAndPlay(EffectInitParam initParam)
        {
            var rTrans = await this.Load(initParam);
            if (rTrans && this.SetPlayParam(rTrans, initParam))
            {
                this.Play(initParam);
            }
        }

        private async UniTask<Transform> Load(EffectInitParam initParam)
        {
            if (!string.IsNullOrEmpty(initParam.effectPath))
            {
                this._effectPath = initParam.effectPath;
                Transform rTrans = null;
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                    var rPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/" + this._effectPath);
                    rTrans = BattleClientUtil.CreateGameobject(rPrefab).transform;
                }
                else
#endif
                {
                    var rEntity = ManagerCenter.Entity.Get(this._effectPath, (int)EntityPoolGroupType.BattleSceneEntity) as BattleSceneEntityView;
                    this.entity = rEntity;
                    await rEntity.Load();
                    // 如果在load期间就销毁了，直接回收
                    if (this.entity != rEntity)
                    {
                        rEntity.Put();
                        return null;
                    }
                    rTrans = rEntity.transform;
                }
                return rTrans;
            }
            return null;
        }

        public bool SetPlayParam(Transform effectTrans, EffectInitParam initParam)
        {
            if (!effectTrans)
            {
                return false;
            }
            if (initParam.parent != null)
            {
                effectTrans.SetParent(initParam.parent);
            }
            if ((initParam.vfxSpace & EVFXSpace.LocalPosition) != 0)
            {
                effectTrans.localPosition = initParam.pos;
            }
            else
            {
                effectTrans.position = initParam.pos;
            }
            if ((initParam.vfxSpace & EVFXSpace.LocalRotation) != 0)
            {
                effectTrans.localRotation = initParam.rotation;
            }
            else
            {
                effectTrans.rotation = initParam.rotation;
            }

            effectTrans.localScale = Vector3.one * initParam.scale;
            this._controller = effectTrans.GetComponent<EffectControlBehaviour>();
            if (!this._controller)
            {
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                    LogGame.LogError("特效控制器未生成");
                }
#endif
                return false;
            }
            this.duration = this._controller.duration;
            return true;
        }

        public static uint GetInstId()
        {
            return ++idGen;
        }

        /// <summary>
        /// 用于将外部赋予controller的effect释放
        /// </summary>
        public void FreeController()
        {
            this._controller = null;
        }
        
        private Transform GetBindPoint(int bindPointId)
        {
            if (this._curPlayParam.bindComp != null)
            {
                return this._curPlayParam.bindComp.GetBindPoint(bindPointId);
            }
            return null;
        }
    }

    public struct EffectInitParam
    {
        public string effectPath;
        public Transform parent;
        public EVFXSpace vfxSpace;
        public Vector3 pos;
        public Quaternion rotation;
        public float scale;
        public bool isLoop;
        public System.Action<Effect> onDestroy;
        // 是否自行播放 ，timeline播放时由timeline控制其播放进度
        public bool playByManager;

        // Laser or Bullet
        public Vector3 targetPos;

        // OtherDummy
        public BattleViewBindPointComponent bindComp;

        public EffectInitParam(string effectPath,
            Transform parent,
            Vector3 pos,
            Quaternion rotation,
            BattleViewBindPointComponent bindComp = null,
            float scale = 1f,
            bool isLoop = false,
            EVFXSpace vfxSpace = EVFXSpace.Local,
            Action<Effect> onDestroy = null,
            bool playBySelf = true)
            : this(effectPath, parent, pos, rotation, Vector3.zero, bindComp, scale, isLoop, vfxSpace, onDestroy, playBySelf)
        {
        }

        public EffectInitParam(string effectPath, Transform parent, Vector3 pos, Quaternion rotation, Vector3 targetPos, BattleViewBindPointComponent bindComp,
            float scale, bool isLoop, EVFXSpace vfxSpace, Action<Effect> onDestroy, bool playByManager)
        {
            this.effectPath = effectPath;
            this.parent = parent;
            this.pos = pos;
            this.rotation = rotation;
            this.scale = scale;
            this.isLoop = isLoop;
            this.vfxSpace = vfxSpace;
            this.onDestroy = onDestroy;
            this.targetPos = targetPos;
            this.playByManager = playByManager;
            this.bindComp = bindComp;
        }

        public static bool operator ==(EffectInitParam param, EffectInitParam param0)
        {
            return param.Equals(param0);
        }

        public static bool operator !=(EffectInitParam param1, EffectInitParam param2) => !(param1 == param2);

        public override bool Equals(object obj)
        {
            return obj is EffectInitParam param &&
                   Equals(param.effectPath, this.effectPath) &&
                   Equals(param.parent, this.parent) &&
                   Equals(param.vfxSpace, this.vfxSpace) &&
                   Equals(param.pos, this.pos) &&
                   Equals(param.rotation, this.rotation) &&
                   Equals(param.scale, this.scale) &&
                   Equals(param.isLoop, this.isLoop) &&
                   Equals(param.onDestroy, this.onDestroy) &&
                   Equals(param.playByManager, this.playByManager) &&
                   Equals(param.bindComp, this.bindComp);
        }

        public override int GetHashCode()
        {
            var nCode = base.GetHashCode();
            nCode = nCode | (this.effectPath?.GetHashCode() ?? 0) |
                    this.vfxSpace.GetHashCode() |
                    this.pos.GetHashCode() |
                    this.rotation.GetHashCode() |
                    this.scale.GetHashCode() |
                    this.isLoop.GetHashCode() |
                    (this.onDestroy?.GetHashCode() ?? 0) |
                    this.playByManager.GetHashCode();
            if (this.parent)
            {
                nCode |= this.parent.GetHashCode();
            }
            if (this.bindComp != null)
            {
                nCode |= this.bindComp.GetHashCode();
            }
            return nCode;
        }
    }

    [Flags]
    public enum EVFXSpace
    {
        Auto = 0,
        LocalPosition = 1 << 0,
        LocalRotation = 1 << 1,
        WorldPosition = 1 << 2,
        WorldRotation = 1 << 3,
        Local = LocalPosition | LocalRotation,
        World = WorldPosition | WorldRotation,
    }
}